package ru.ganyaev.external_process;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class BasicExternalProcess extends ExternalProcess {
    private static final Logger LOGGER = LogManager.getRootLogger();
    private Charset osCharset;

    public BasicExternalProcess() {
        this.osCharset = Charset.defaultCharset();
    }

    public BasicExternalProcess(Charset osCharset) {
        this.osCharset = osCharset;
    }

    @Override
    public ExternalProcessResult execute(Command command)
            throws ExecuteExternallProcessException {
        LOGGER.debug("Try to execute command: " + command);
        ProcessBuilder setUpOver = new ProcessBuilder(
                command.getCommandAsList());
        Process process;
        try {
            process = setUpOver.start();
        } catch (IOException e) {
            throw new ExecuteExternallProcessException(
                    "Can not start process.", e);
        }

        BufferedReader stdOutputReader = new BufferedReader(
                new InputStreamReader(process.getInputStream(), osCharset));

        BufferedReader stdErrorReader = new BufferedReader(
                new InputStreamReader(process.getErrorStream(), osCharset));

        // read the output from the command
        String stdOutputString = null;
        List<String> stdOutputList = new ArrayList<String>();
        try {
            while ((stdOutputString = stdOutputReader.readLine()) != null) {
                stdOutputList.add(stdOutputString);
            }
        } catch (IOException e) {
            throw new ExecuteExternallProcessException(
                    "Can not read std imput string.", e);
        }

        // read any errors from the attempted command
        String stdErrorString = null;
        List<String> stdErrorList = new ArrayList<String>();
        try {
            while ((stdErrorString = stdErrorReader.readLine()) != null) {
                stdErrorList.add(stdErrorString);
            }
        } catch (IOException e) {
            throw new ExecuteExternallProcessException(
                    "Can not read std error string", e);
        }

        Integer exitCode = null;
        try {
            exitCode = process.waitFor();
        } catch (InterruptedException e) {
            throw new ExecuteExternallProcessException(
                    "Can not get exit code.", e);
        }

        return new BasicExternalProcessResult(exitCode, stdOutputList,
                stdErrorList);
    }

    @Override
    public void executeAndCheckExitCode(Command command)
            throws ExecuteExternallProcessException {
        ExternalProcessResult result = execute(command);
        LOGGER.debug("Executed result of command: " + result);
        if (!result.isValid()) {
            throw new ExecuteExternallProcessException(
                    "Result of external process in not valid. " + result);
        }
    }
}
